package com.example.servicegateway.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 限流过滤器
 * 
 * 功能说明：
 * 1. 对API请求进行限流控制，防止系统过载
 * 2. 支持基于IP地址的限流
 * 3. 支持基于用户ID的限流
 * 4. 提供可配置的限流策略
 * 
 * @author Spring Cloud Demo
 * @version 1.0
 * @since 2024-01-01
 */
@Component
public class RateLimitFilter implements GlobalFilter, Ordered {

    private static final Logger logger = LoggerFactory.getLogger(RateLimitFilter.class);

    /**
     * 限流计数器，用于记录每个客户端的请求次数
     * Key: 客户端标识（IP地址或用户ID）
     * Value: 请求计数器
     */
    private final ConcurrentHashMap<String, AtomicInteger> requestCounters = new ConcurrentHashMap<>();

    /**
     * 限流计数器，用于记录每个客户端的限流时间窗口
     * Key: 客户端标识（IP地址或用户ID）
     * Value: 时间窗口开始时间戳
     */
    private final ConcurrentHashMap<String, Long> timeWindows = new ConcurrentHashMap<>();

    /**
     * 限流配置
     */
    private static final int MAX_REQUESTS_PER_MINUTE = 100;  // 每分钟最大请求数
    private static final long TIME_WINDOW_MS = 60 * 1000;    // 时间窗口（毫秒）

    /**
     * 过滤器执行方法
     * 
     * @param exchange 服务器Web交换对象
     * @param chain 过滤器链
     * @return Mono<Void> 异步处理结果
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        
        // 获取客户端标识（优先使用用户ID，其次使用IP地址）
        String clientId = getClientId(request);
        
        // 检查限流
        if (isRateLimited(clientId)) {
            logger.warn("🚫 客户端 {} 触发限流", clientId);
            return rateLimitExceeded(exchange);
        }
        
        // 增加请求计数
        incrementRequestCount(clientId);
        
        // 继续执行过滤器链
        return chain.filter(exchange);
    }

    /**
     * 获取过滤器执行顺序
     * 
     * @return 执行顺序
     */
    @Override
    public int getOrder() {
        return -50; // 在认证过滤器之后执行
    }

    /**
     * 获取客户端标识
     * 
     * @param request 请求对象
     * @return 客户端标识
     */
    private String getClientId(ServerHttpRequest request) {
        // 优先从请求头中获取用户ID
        String userId = request.getHeaders().getFirst("X-User-Id");
        if (userId != null && !userId.isEmpty()) {
            return "user:" + userId;
        }
        
        // 如果没有用户ID，则使用IP地址
        String ipAddress = getClientIpAddress(request);
        return "ip:" + ipAddress;
    }

    /**
     * 获取客户端IP地址
     * 
     * @param request 请求对象
     * @return 客户端IP地址
     */
    private String getClientIpAddress(ServerHttpRequest request) {
        // 尝试从各种请求头中获取真实IP地址
        String xForwardedFor = request.getHeaders().getFirst("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeaders().getFirst("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty()) {
            return xRealIp;
        }
        
        // 如果都没有，则使用远程地址
        return request.getRemoteAddress() != null ? 
               request.getRemoteAddress().getAddress().getHostAddress() : "unknown";
    }

    /**
     * 检查是否触发限流
     * 
     * @param clientId 客户端标识
     * @return true表示触发限流，false表示未触发限流
     */
    private boolean isRateLimited(String clientId) {
        long currentTime = System.currentTimeMillis();
        
        // 获取或创建时间窗口
        Long windowStart = timeWindows.computeIfAbsent(clientId, k -> currentTime);
        
        // 检查是否需要重置时间窗口
        if (currentTime - windowStart >= TIME_WINDOW_MS) {
            // 重置时间窗口和计数器
            timeWindows.put(clientId, currentTime);
            requestCounters.remove(clientId);
            return false;
        }
        
        // 获取当前请求计数
        AtomicInteger counter = requestCounters.get(clientId);
        if (counter == null) {
            return false;
        }
        
        // 检查是否超过限流阈值
        return counter.get() >= MAX_REQUESTS_PER_MINUTE;
    }

    /**
     * 增加请求计数
     * 
     * @param clientId 客户端标识
     */
    private void incrementRequestCount(String clientId) {
        AtomicInteger counter = requestCounters.computeIfAbsent(clientId, k -> new AtomicInteger(0));
        int currentCount = counter.incrementAndGet();
        
        // 记录请求计数（可选，用于调试）
        if (currentCount % 10 == 0) {
            logger.debug("📊 客户端 {} 当前请求计数: {}", clientId, currentCount);
        }
    }

    /**
     * 返回限流响应
     * 
     * @param exchange 服务器Web交换对象
     * @return Mono<Void> 异步处理结果
     */
    private Mono<Void> rateLimitExceeded(ServerWebExchange exchange) {
        exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
        
        // 添加限流响应头
        exchange.getResponse().getHeaders().add("X-RateLimit-Limit", String.valueOf(MAX_REQUESTS_PER_MINUTE));
        exchange.getResponse().getHeaders().add("X-RateLimit-Remaining", "0");
        exchange.getResponse().getHeaders().add("X-RateLimit-Reset", 
                String.valueOf(System.currentTimeMillis() + TIME_WINDOW_MS));
        
        return exchange.getResponse().setComplete();
    }

    /**
     * 获取当前限流统计信息（用于监控）
     * 
     * @return 限流统计信息
     */
    public String getRateLimitStats() {
        StringBuilder stats = new StringBuilder();
        stats.append("限流统计信息:\n");
        stats.append("当前活跃客户端数: ").append(requestCounters.size()).append("\n");
        stats.append("时间窗口大小: ").append(TIME_WINDOW_MS / 1000).append("秒\n");
        stats.append("每分钟最大请求数: ").append(MAX_REQUESTS_PER_MINUTE).append("\n");
        
        requestCounters.forEach((clientId, counter) -> {
            stats.append("客户端 ").append(clientId)
                 .append(": ").append(counter.get()).append(" 次请求\n");
        });
        
        return stats.toString();
    }
} 